{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# The Workspace and Projects\n",
    "\n",
    "```{caution}\n",
    "You probably don't need to worry about these features until your simulations get large and complicated.\n",
    "```\n",
    "\n",
    "The *Workspace* is equivalent to a web browser window, while a *Project* is like tabs inside the browser. Each *Project* is an isolated OpenPNM simulation with a single *Network* and all associated objects.  All *Projects* are stored in the same *Workspace*. There can be only 1 *Workspace* open at a given time, so all new projects are registered in the same *Workspace*.  *Projects* and *Workspaces* can be saved and loaded."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2021-06-24T11:25:16.232913Z",
     "iopub.status.busy": "2021-06-24T11:25:16.231307Z",
     "iopub.status.idle": "2021-06-24T11:25:16.814306Z",
     "shell.execute_reply": "2021-06-24T11:25:16.812842Z"
    }
   },
   "outputs": [],
   "source": [
    "import openpnm as op\n",
    "op.visualization.set_mpl_style()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Usage of Projects and Workspace\n",
    "\n",
    "Initialize the *Workspace* and save in a variable called ``ws``, and print it to verify that it is currently empty:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2021-06-24T11:25:16.820923Z",
     "iopub.status.busy": "2021-06-24T11:25:16.819453Z",
     "iopub.status.idle": "2021-06-24T11:25:16.827591Z",
     "shell.execute_reply": "2021-06-24T11:25:16.826509Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n",
      "\n"
     ]
    }
   ],
   "source": [
    "ws = op.Workspace()\n",
    "print(ws)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You don't need to create a project, as they get automatically created each time a new network is initialized:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2021-06-24T11:25:16.860066Z",
     "iopub.status.busy": "2021-06-24T11:25:16.858649Z",
     "iopub.status.idle": "2021-06-24T11:25:16.862976Z",
     "shell.execute_reply": "2021-06-24T11:25:16.861778Z"
    }
   },
   "outputs": [],
   "source": [
    "pn = op.network.Cubic(shape=[4, 4, 4])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This created a new project and added the network to it automatically:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2021-06-24T11:25:16.869809Z",
     "iopub.status.busy": "2021-06-24T11:25:16.868559Z",
     "iopub.status.idle": "2021-06-24T11:25:16.873139Z",
     "shell.execute_reply": "2021-06-24T11:25:16.874138Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "══════════════════════════════════════════════════════════════════════════════\n",
      "Object Name : Object Class and ID\n",
      "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n",
      "net : <openpnm.network.Cubic at 0x17c8d70bbd0>\n",
      "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n"
     ]
    }
   ],
   "source": [
    "print(pn.project)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It is possible to create an empty project and tell the network initialization to use it, but this is not usually necessary:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "proj = ws.new_project()\n",
    "pn2 = op.network.Demo(project=proj)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can view all active projects by printing the workspace via:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2021-06-24T11:25:16.911258Z",
     "iopub.status.busy": "2021-06-24T11:25:16.909807Z",
     "iopub.status.idle": "2021-06-24T11:25:16.915658Z",
     "shell.execute_reply": "2021-06-24T11:25:16.914634Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n",
      "proj_01\n",
      "══════════════════════════════════════════════════════════════════════════════\n",
      "Object Name : Object Class and ID\n",
      "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n",
      "net : <openpnm.network.Cubic at 0x17c8d70bbd0>\n",
      "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n",
      "proj_02\n",
      "══════════════════════════════════════════════════════════════════════════════\n",
      "Object Name : Object Class and ID\n",
      "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n",
      "net : <openpnm.network.Demo at 0x17c87766ae0>\n",
      "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(ws)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A project can be purged from the workspace via `ws.close_project(proj)`. Let's print workspace again,:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2021-06-24T11:25:16.923204Z",
     "iopub.status.busy": "2021-06-24T11:25:16.922248Z",
     "iopub.status.idle": "2021-06-24T11:25:16.927033Z",
     "shell.execute_reply": "2021-06-24T11:25:16.927981Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n",
      "proj_01\n",
      "══════════════════════════════════════════════════════════════════════════════\n",
      "Object Name : Object Class and ID\n",
      "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n",
      "net : <openpnm.network.Cubic at 0x17c8d70bbd0>\n",
      "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n",
      "\n"
     ]
    }
   ],
   "source": [
    "ws.close_project(proj)\n",
    "print(ws)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Workspace is a Singleton\n",
    "\n",
    "The `Workspace` object is a [singleton](https://python-patterns.guide/gang-of-four/singleton/), which is \"design pattern\" where only ONE instance of an object can be created in any given session. The reason OpenPNM uses this pattern is to enable the lookup of object relationships, such as which project a network belongs to or what network is associated with a phase.  This is done as follows.  Let's first create a new network:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pn = op.network.Demo(shape=[3, 3, 3])\n",
    "print(pn.name)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's scan through each `project` in the `workspace` until we find the one that contains `pn`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "══════════════════════════════════════════════════════════════════════════════\n",
      "Object Name : Object Class and ID\n",
      "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n",
      "net : <openpnm.network.Demo at 0x17c8ddbff40>\n",
      "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\n"
     ]
    }
   ],
   "source": [
    "ws = op.Workspace()\n",
    "for project in ws.values():\n",
    "    for item in project:\n",
    "        if item is pn:\n",
    "            # If this were a function, here would be \"return project\"\n",
    "            break\n",
    "print(project)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We refer to this as a \"bottom-up\" approach since the object is basically looking for itself. A \"top-down\" approach would be the case where relationships are hard-coded such that `phase.project` actually contains a handle to the project (i.e. `phase.project = proj`). In the \"bottom-up\" approach, accessing `phase.project` actually triggers a function that does the above search.  \n",
    "\n",
    "Similarly we can find the network associated with a given phase (or algorithm) as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "net : <openpnm.network.Demo at 0x17c8ddbff40>"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ws = op.Workspace()\n",
    "air = op.phase.Air(network=pn)\n",
    "for project in ws.values():\n",
    "    for item in project:\n",
    "        if item is pn:\n",
    "            # If this were a function, here would be \"return item\"\n",
    "            break\n",
    "pn"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The above two lookups may seem convoluted, but they have the benefit of not requiring that `pn` have an attribute containing handles to all associated objects. It was found in V1 that this lead to memory leaks since all objects contained references to other objects, and it was very tricky to actually delete something. The bottom-up search avoids this, and with the help of Python's \"syntactic sugar\" we can make it all occur behind the scenes and uphold the appearance that you're accessing the object directly."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## One Network per Project\n",
    "\n",
    "Only \n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": true,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
